package com.cs.deviceManager.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cs.deviceManager.domain.DevDevice;
import com.cs.deviceManager.domain.DevDeviceCommand;
import com.cs.deviceManager.mapper.DevDeviceCommandMapper;
import com.cs.deviceManager.mapper.DevDeviceMapper;
import com.cs.deviceManager.mqtt.MqttUtil;
import com.cs.deviceManager.service.IDevDeviceCommandService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 设备指令Service实现类
 * 实现间歇性设备控制指令的业务逻辑
 *
 * @author zizige
 * @date 2025-06-30
 */
@Service
public class DevDeviceCommandServiceImpl implements IDevDeviceCommandService {
    private static final Logger log = LoggerFactory.getLogger(DevDeviceCommandServiceImpl.class);

    @Resource
    private DevDeviceCommandMapper devDeviceCommandMapper;

    @Resource
    private DevDeviceMapper devDeviceMapper;
    
    // 注入自身实例，用于解决事务自调用问题
    @Resource
    private DevDeviceCommandServiceImpl self;

    @Value("${mqtt.client-id:device-status-listener}")
    private String clientId;

    // 设备活跃状态管理 - 使用ConcurrentHashMap确保线程安全
    private static final ConcurrentHashMap<String, DeviceActiveInfo> DEVICE_ACTIVE_STATUS = new ConcurrentHashMap<>();
    // 设备活跃窗口时长(秒)
    private static final int DEVICE_ACTIVE_WINDOW = 30;
    // 指令超时时间(秒)
    private static final int COMMAND_TIMEOUT = 60;
    // 指令过期时间(分钟)
    private static final int COMMAND_EXPIRE_MINUTES = 10;

    // 延迟队列 - 用于指令超时监控
    private static final DelayQueue<DelayedCommand> COMMAND_TIMEOUT_QUEUE = new DelayQueue<>();

    /**
     * 查询设备指令
     */
    @Override
    public DevDeviceCommand selectDevDeviceCommandByCommandId(Long commandId) {
        return devDeviceCommandMapper.selectDevDeviceCommandByCommandId(commandId);
    }

    /**
     * 查询设备指令列表
     */
    @Override
    public List<DevDeviceCommand> selectDevDeviceCommandList(DevDeviceCommand devDeviceCommand) {
        return devDeviceCommandMapper.selectDevDeviceCommandList(devDeviceCommand);
    }

    /**
     * 新增设备指令
     */
    @Override
    @Transactional
    public int insertDevDeviceCommand(DevDeviceCommand devDeviceCommand) {
        return devDeviceCommandMapper.insertDevDeviceCommand(devDeviceCommand);
    }

    /**
     * 修改设备指令
     */
    @Override
    @Transactional
    public int updateDevDeviceCommand(DevDeviceCommand devDeviceCommand) {
        return devDeviceCommandMapper.updateDevDeviceCommand(devDeviceCommand);
    }

    /**
     * 批量删除设备指令
     */
    @Override
    @Transactional
    public int deleteDevDeviceCommandByCommandIds(String[] commandIds) {
        return devDeviceCommandMapper.deleteDevDeviceCommandByCommandIds(commandIds);
    }

    /**
     * 删除设备指令信息
     */
    @Override
    @Transactional
    public int deleteDevDeviceCommandByCommandId(Long commandId) {
        return devDeviceCommandMapper.deleteDevDeviceCommandByCommandId(commandId);
    }

    /**
     * 创建新指令
     * 对应xx.md文档中指令管理模块的createCommand()关键流程
     */
    @Override
    @Transactional
    public DevDeviceCommand createCommand(String deviceId, String deviceNumber, String commandType,
                                          String commandContent, Integer maxRetryCount) {
        log.info("创建新设备指令: deviceId={}, commandType={}", deviceId, commandType);

        // 创建指令对象
        DevDeviceCommand command = new DevDeviceCommand();
        command.setDeviceId(deviceId);
        command.setDeviceNumber(deviceNumber);
        command.setCommandType(commandType);
        command.setCommandContent(commandContent);
        command.setStatus("WAITING"); // 初始状态为待执行
        command.setRetryCount(0);
        command.setMaxRetryCount(maxRetryCount != null ? maxRetryCount : 3);

        // 设置指令过期时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, COMMAND_EXPIRE_MINUTES);
        command.setExpireTime(calendar.getTime());

        // 保存到数据库
        insertDevDeviceCommand(command);

        // 如果设备当前活跃，则立即下发指令
        if (isDeviceActive(deviceId)) {
            self.sendCommandToDevice(command);
        }

        return command;
    }

    /**
     * 获取待执行指令
     * 对应xx.md文档中指令管理模块的getPendingCommand()关键流程
     */
    @Override
    public List<DevDeviceCommand> getPendingCommands(String deviceId) {
        return devDeviceCommandMapper.selectPendingCommandsByDeviceId(deviceId);
    }

    /**
     * 更新指令状态
     * 对应xx.md文档中指令管理模块的updateCommandStatus()关键流程
     */
    @Override
    @Transactional
    public int updateCommandStatus(Long commandId, String status) {
        log.info("更新指令状态: commandId={}, status={}", commandId, status);
        int result = devDeviceCommandMapper.updateCommandStatus(commandId, status);

        // 如果状态为已下发，则启动超时监控
        if ("SENT".equals(status)) {
            startTimeoutMonitor(commandId);
        }
        // 如果状态为最终状态（成功、失败），则取消超时监控
        else if ("SUCCESS".equals(status) || "FAILED".equals(status)) {
            // 这里可以添加逻辑移除超时监控
        }

        return result;
    }

    /**
     * 标记设备为活跃状态
     * 对应xx.md文档中设备状态模块的markDeviceActive()关键流程
     */
    @Override
    public boolean markDeviceActive(String deviceId) {
        log.info("标记设备为活跃状态: deviceId={}", deviceId);

        // 更新设备活跃状态
        DeviceActiveInfo activeInfo = new DeviceActiveInfo();
        activeInfo.setLastActiveTime(System.currentTimeMillis());
        DEVICE_ACTIVE_STATUS.put(deviceId, activeInfo);

        // 检查并下发待执行指令
        List<DevDeviceCommand> pendingCommands = getPendingCommands(deviceId);
        for (DevDeviceCommand command : pendingCommands) {
            self.sendCommandToDevice(command);
        }

        return true;
    }

    /**
     * 查询设备是否活跃
     * 对应xx.md文档中设备状态模块的isDeviceActive()关键流程
     */
    @Override
    public boolean isDeviceActive(String deviceId) {
        DeviceActiveInfo activeInfo = DEVICE_ACTIVE_STATUS.get(deviceId);
        if (activeInfo == null) {
            return false;
        }

        // 检查是否在活跃窗口内
        long currentTime = System.currentTimeMillis();
        long activeWindowStart = activeInfo.getLastActiveTime();
        long activeWindowEnd = activeWindowStart + (DEVICE_ACTIVE_WINDOW * 1000);

        return currentTime >= activeWindowStart && currentTime <= activeWindowEnd;
    }

    /**
     * 检查设备超时
     * 对应xx.md文档中设备状态模块的checkDeviceTimeout()关键流程
     */
    @Override
    public boolean checkDeviceTimeout(String deviceId) {
        DeviceActiveInfo activeInfo = DEVICE_ACTIVE_STATUS.get(deviceId);
        if (activeInfo == null) {
            return true; // 未记录活跃信息，视为超时
        }

        long currentTime = System.currentTimeMillis();
        long activeWindowEnd = activeInfo.getLastActiveTime() + (DEVICE_ACTIVE_WINDOW * 1000);

        // 如果当前时间超过活跃窗口结束时间，则认为设备已休眠
        if (currentTime > activeWindowEnd) {
            log.info("设备已休眠: deviceId={}", deviceId);
            return true;
        }

        return false;
    }

    /**
     * 发送指令到设备
     * 对应xx.md文档中通信处理模块的sendCommandToDevice()关键流程
     */
    @Override
    @Transactional
    public boolean sendCommandToDevice(DevDeviceCommand command) {
        try {
            log.info("发送指令到设备: deviceId={}, commandId={}", command.getDeviceId(), command.getCommandId());

            // 检查设备是否活跃
            if (!isDeviceActive(command.getDeviceId())) {
                log.info("设备当前不活跃，将指令保存等待设备活跃时发送: deviceId={}", command.getDeviceId());
                // 保持指令状态为WAITING，等待设备活跃时通过markDeviceActive方法自动发送
                return true;
            }

            // 对于CONFIG_UPDATE指令，进行参数处理
            String processedContent = command.getCommandContent();
            if ("CONFIG_UPDATE".equals(command.getCommandType())) {
                processedContent = processConfigUpdateCommand(command.getCommandContent());
            }

            DevDevice dev = devDeviceMapper.selectDevDeviceByDevId(Long.valueOf(command.getDeviceId()));

            // 使用MqttUtil发送指令
            String topic = "device/" + dev.getDevNumber() + "/cmd";
            boolean sendResult = MqttUtil.publish(clientId, topic, processedContent);

            if (sendResult) {
                // 更新指令状态为已下发
                command.setStatus("SENT");
                command.setSendTime(new Date());
                updateDevDeviceCommand(command);

                // 启动超时监控
                startTimeoutMonitor(command.getCommandId());

                log.info("指令发送成功: commandId={}", command.getCommandId());
            }

            return sendResult;
        } catch (Exception e) {
            log.error("发送指令失败: commandId={}", command.getCommandId(), e);
            return false;
        }
    }

    /**
     * 处理CONFIG_UPDATE类型指令
     * 解析指令内容中的参数(如Heart_T)，并进行验证
     *
     * @param commandContent 指令内容(JSON格式)
     * @return 处理后的指令内容
     */
    private String processConfigUpdateCommand(String commandContent) {
        try {
            return "";
        } catch (Exception e) {
            log.error("处理CONFIG_UPDATE指令失败: {}", commandContent, e);
            return commandContent; // 发生异常时返回原始内容
        }
    }

    /**
     * 从指令内容中提取设备ID
     *
     * @param commandContent 指令内容
     * @return 设备ID，如果无法提取则返回null
     */
    private String extractDeviceIdFromCommand(String commandContent) {
        try {
            // 尝试从指令内容中提取设备ID
            // 这里的实现方式取决于指令内容的格式
            // 例如，如果指令内容中包含deviceId字段，则可以直接提取
            JSONObject json = JSONUtil.parseObj(commandContent);
            if (json.containsKey("deviceId")) {
                return json.getStr("deviceId");
            }
        } catch (Exception e) {
            log.error("从指令内容中提取设备ID失败: {}", commandContent, e);
        }
        return null;
    }

    /**
     * 处理设备数据
     * 对应xx.md文档中通信处理模块的handleDeviceData()关键流程
     */
    @Override
    @Transactional
    public boolean handleDeviceData(String deviceNumber, String data) {
        try {
            log.info("处理设备数据: deviceNumber={}", deviceNumber);

            DevDevice dev = devDeviceMapper.selectDevDeviceByDevNumber(deviceNumber);

            if(dev == null)
            {
                log.error("找不到设备编号: deviceNumber={}", deviceNumber);
            }

            // 标记设备为活跃状态
            self.markDeviceActive(String.valueOf(Objects.requireNonNull(dev).getDevId()));

            // 解析数据，判断是否为指令反馈
            // 这里需要根据实际的数据格式进行解析
            // 假设数据中包含commandId和result字段

            // 示例：检查数据是否包含指令反馈信息
            if (data.contains("commandId") && data.contains("result")) {
                // 解析指令ID和执行结果
                // 实际项目中应该使用JSON解析库进行解析
                Long commandId = extractCommandId(data);
                String result = extractResult(data);

                if (commandId != null) {
                    // 更新指令状态
                    DevDeviceCommand command = selectDevDeviceCommandByCommandId(commandId);
                    if (command != null) {
                        if ("success".equals(result)) {
                            command.setStatus("SUCCESS");
                            command.setExecuteTime(new Date());
                            command.setFeedbackResult(data);
                            updateDevDeviceCommand(command);
                        } else if ("failed".equals(result)) {
                            handleCommandFailure(commandId, data);
                        }
                    }
                }
            }

            return true;
        } catch (Exception e) {
            log.error("处理设备数据失败: deviceNumber={}", deviceNumber, e);
            return false;
        }
    }

    /**
     * 处理连接异常
     * 对应xx.md文档中通信处理模块的handleConnectionLost()关键流程
     */
    @Override
    public void handleConnectionLost(String deviceId) {
        log.warn("设备连接断开: deviceId={}", deviceId);
        // 可以添加连接断开处理逻辑，如记录日志、触发告警等
    }

    /**
     * 启动超时监控
     * 对应xx.md文档中超时重试模块的startTimeoutMonitor()关键流程
     */
    @Override
    public void startTimeoutMonitor(Long commandId) {
        log.info("启动指令超时监控: commandId={}", commandId);

        // 创建延迟任务并添加到队列
        DelayedCommand delayedCommand = new DelayedCommand(commandId, COMMAND_TIMEOUT);
        COMMAND_TIMEOUT_QUEUE.offer(delayedCommand);
    }

    /**
     * 处理指令超时
     * 对应xx.md文档中超时重试模块的handleCommandTimeout()关键流程
     */
    @Override
    @Transactional
    public void handleCommandTimeout(Long commandId) {
        log.warn("指令超时: commandId={}", commandId);

        // 查询指令信息
        DevDeviceCommand command = selectDevDeviceCommandByCommandId(commandId);
        if (command == null) {
            log.warn("超时指令不存在: commandId={}", commandId);
            return;
        }

        // 检查指令是否已完成
        if ("SUCCESS".equals(command.getStatus()) || "FAILED".equals(command.getStatus())) {
            return;
        }

        // 增加重试次数
        devDeviceCommandMapper.incrementRetryCount(commandId);
        command.setRetryCount(command.getRetryCount() + 1);

        // 检查是否达到最大重试次数
        if (command.getRetryCount() >= command.getMaxRetryCount()) {
            // 达到最大重试次数，标记为失败
            log.error("指令达到最大重试次数，标记为失败: commandId={}", commandId);
            command.setStatus("FAILED");
            command.setExecuteTime(new Date());
            command.setFeedbackResult("达到最大重试次数");
            updateDevDeviceCommand(command);
        } else {
            // 未达到最大重试次数，标记为超时，等待下次设备活跃时重试
            log.info("指令将在设备下次活跃时重试: commandId={}, retryCount={}",
                    commandId, command.getRetryCount());
            command.setStatus("TIMEOUT");
            updateDevDeviceCommand(command);
        }
    }

    /**
     * 执行指令重试
     * 对应xx.md文档中超时重试模块的retryCommand()关键流程
     */
    @Override
    @Transactional
    public boolean retryCommand(Long commandId) {
        log.info("执行指令重试: commandId={}", commandId);

        // 查询指令信息
        DevDeviceCommand command = selectDevDeviceCommandByCommandId(commandId);
        if (command == null) {
            log.warn("重试指令不存在: commandId={}", commandId);
            return false;
        }

        // 检查指令是否可重试
        if (!"TIMEOUT".equals(command.getStatus()) && !"FAILED".equals(command.getStatus())) {
            log.warn("指令当前状态不支持重试: commandId={}, status={}", commandId, command.getStatus());
            return false;
        }

        // 检查是否达到最大重试次数
        if (command.getRetryCount() >= command.getMaxRetryCount()) {
            log.warn("指令已达到最大重试次数: commandId={}", commandId);
            return false;
        }

        // 检查指令是否已过期
        if (new Date().after(command.getExpireTime())) {
            log.warn("指令已过期: commandId={}", commandId);
            command.setStatus("FAILED");
            command.setExecuteTime(new Date());
            command.setFeedbackResult("指令已过期");
            updateDevDeviceCommand(command);
            return false;
        }

        // 增加重试次数
        devDeviceCommandMapper.incrementRetryCount(commandId);
        command.setRetryCount(command.getRetryCount() + 1);

        // 尝试重新发送指令
        return self.sendCommandToDevice(command);
    }

    /**
     * 处理指令执行失败
     */
    private void handleCommandFailure(Long commandId, String feedback) {
        DevDeviceCommand command = selectDevDeviceCommandByCommandId(commandId);
        if (command != null) {
            // 增加重试次数
            devDeviceCommandMapper.incrementRetryCount(commandId);
            command.setRetryCount(command.getRetryCount() + 1);
            command.setFeedbackResult(feedback);

            // 检查是否达到最大重试次数
            if (command.getRetryCount() >= command.getMaxRetryCount()) {
                command.setStatus("FAILED");
                command.setExecuteTime(new Date());
            } else {
                command.setStatus("TIMEOUT"); // 等待下次设备活跃时重试
            }

            updateDevDeviceCommand(command);
        }
    }

    /**
     * 从数据中提取指令ID（示例方法）
     */
    private Long extractCommandId(String data) {
        // 实际项目中应该使用JSON解析库进行解析
        // 这里仅作为示例
        try {
            if (data.contains("commandId")) {
                int startIndex = data.indexOf("commandId") + 11;
                int endIndex = data.indexOf(",", startIndex);
                if (endIndex == -1) {
                    endIndex = data.indexOf("}", startIndex);
                }
                String commandIdStr = data.substring(startIndex, endIndex).trim();
                return Long.parseLong(commandIdStr);
            }
        } catch (Exception e) {
            log.error("解析指令ID失败: {}", data, e);
        }
        return null;
    }

    /**
     * 从数据中提取执行结果（示例方法）
     */
    private String extractResult(String data) {
        // 实际项目中应该使用JSON解析库进行解析
        // 这里仅作为示例
        try {
            if (data.contains("result")) {
                int startIndex = data.indexOf("result") + 8;
                int endIndex = data.indexOf(",", startIndex);
                if (endIndex == -1) {
                    endIndex = data.indexOf("}", startIndex);
                }
                // 去除引号
                return data.substring(startIndex, endIndex).trim().replace("\"", "");
            }
        } catch (Exception e) {
            log.error("解析执行结果失败: {}", data, e);
        }
        return null;
    }

    /**
     * 设备活跃信息类
     */
    private static class DeviceActiveInfo {
        private long lastActiveTime; // 最后活跃时间戳

        public long getLastActiveTime() {
            return lastActiveTime;
        }

        public void setLastActiveTime(long lastActiveTime) {
            this.lastActiveTime = lastActiveTime;
        }
    }

    /**
     * 延迟指令类 - 用于超时监控
     */
    private static class DelayedCommand implements Delayed {
        private final Long commandId;
        private final long expireTime;

        public DelayedCommand(Long commandId, int timeoutSeconds) {
            this.commandId = commandId;
            this.expireTime = System.currentTimeMillis() + (timeoutSeconds * 1000);
        }

        @Override
        public long getDelay(TimeUnit unit) {
            long diff = expireTime - System.currentTimeMillis();
            return unit.convert(diff, TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return Long.compare(this.expireTime, ((DelayedCommand) o).expireTime);
        }

        public Long getCommandId() {
            return commandId;
        }
    }

    /**
     * 定时任务 - 检查指令超时
     */
    @Scheduled(fixedRate = 1000) // 每秒检查一次
    public void checkCommandTimeout() {
        try {
            DelayedCommand delayedCommand;
            while ((delayedCommand = COMMAND_TIMEOUT_QUEUE.poll()) != null) {
                // 处理超时指令
                self.handleCommandTimeout(delayedCommand.getCommandId());
            }
        } catch (Exception e) {
            log.error("检查指令超时异常", e);
        }
    }

    /**
     * 定时任务 - 清理过期指令
     */
    @Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次
    @Transactional
    public void cleanupExpiredCommands() {
        try {
            log.info("清理过期指令");
            List<DevDeviceCommand> expiredCommands = devDeviceCommandMapper.selectExpiredCommands(new Date());
            for (DevDeviceCommand command : expiredCommands) {
                // 更新过期指令状态为失败
                if (!"SUCCESS".equals(command.getStatus()) && !"FAILED".equals(command.getStatus())) {
                    command.setStatus("FAILED");
                    command.setExecuteTime(new Date());
                    command.setFeedbackResult("指令已过期");
                    updateDevDeviceCommand(command);
                }
            }
        } catch (Exception e) {
            log.error("清理过期指令异常", e);
        }
    }

    /**
     * 定时任务 - 清理过期设备活跃状态
     */
    @Scheduled(cron = "0 0/1 * * * ?") // 每分钟执行一次
    public void cleanupExpiredDeviceStatus() {
        try {
            log.debug("清理过期设备活跃状态");
            long currentTime = System.currentTimeMillis();
            long expireTimeThreshold = currentTime - (DEVICE_ACTIVE_WINDOW * 1000 * 2); // 2倍活跃窗口时间

            Iterator<Map.Entry<String, DeviceActiveInfo>> iterator = DEVICE_ACTIVE_STATUS.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, DeviceActiveInfo> entry = iterator.next();
                if (entry.getValue().getLastActiveTime() < expireTimeThreshold) {
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            log.error("清理过期设备活跃状态异常", e);
        }
    }
}